use crate::error::{CompilerError, Result};
use crate::llvm_backend::LLVMBackend;
use inkwell::{values::BasicValueEnum, AddressSpace, IntPredicate};

impl<'ctx> LLVMBackend<'ctx> {

    /// Generate string.to_uppercase() method call
    /// Converts the string to uppercase and returns the result
    ///
    /// Example: "hello".to_uppercase() returns "HELLO"
    pub(in crate::llvm_backend) fn generate_string_to_uppercase_method(
        &mut self,
        var_name: &str,
    ) -> Result<BasicValueEnum<'ctx>> {
        // Get the string pointer
        let string_ptr = if let Some(var_value) = self.variable_map.get(var_name) {
            var_value.into_pointer_value()
        } else {
            return Err(CompilerError::internal(&format!(
                "Undefined variable: {}",
                var_name
            )));
        };

        // Load the string pointer value (i8*)
        let i8_ptr_type = self.context.i8_type().ptr_type(AddressSpace::default());
        let string_value = self
            .builder
            .build_load(i8_ptr_type, string_ptr, "string_val")?;
        let original_str_ptr = string_value.into_pointer_value();

        let i8_type = self.context.i8_type();
        let i32_type = self.context.i32_type();
        let i64_type = self.context.i64_type();

        // Get strlen function
        let strlen_fn_type = i32_type.fn_type(&[i8_ptr_type.into()], false);
        let strlen_fn = if let Some(func) = self.module.get_function("strlen") {
            func
        } else {
            self.module.add_function("strlen", strlen_fn_type, None)
        };

        // Get string length
        let str_len_i32 = self
            .builder
            .build_call(strlen_fn, &[original_str_ptr.into()], "str_len_i32")?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_int_value();
        let str_len = self
            .builder
            .build_int_z_extend(str_len_i32, i64_type, "str_len")?;

        // Create basic blocks
        let current_func = self.current_function.unwrap();
        let empty_check_block = self
            .context
            .append_basic_block(current_func, "to_upper_empty_check");
        let alloc_block = self
            .context
            .append_basic_block(current_func, "to_upper_alloc");
        let copy_loop = self
            .context
            .append_basic_block(current_func, "to_upper_copy_loop");
        let copy_body = self
            .context
            .append_basic_block(current_func, "to_upper_copy_body");
        let copy_loop_end = self
            .context
            .append_basic_block(current_func, "to_upper_copy_loop_end");
        let return_block = self
            .context
            .append_basic_block(current_func, "to_upper_return");

        // Jump to empty check
        self.builder.build_unconditional_branch(empty_check_block)?;

        // Empty check: if len == 0, return empty string
        self.builder.position_at_end(empty_check_block);
        let is_empty = self.builder.build_int_compare(
            IntPredicate::EQ,
            str_len,
            i64_type.const_int(0, false),
            "is_empty",
        )?;
        self.builder
            .build_conditional_branch(is_empty, return_block, alloc_block)?;

        // Allocate memory: str_len + 1 (for null terminator)
        self.builder.position_at_end(alloc_block);
        let total_with_null =
            self.builder
                .build_int_add(str_len, i64_type.const_int(1, false), "total_with_null")?;

        // Allocate memory using malloc
        let malloc_fn_type = i8_ptr_type.fn_type(&[i64_type.into()], false);
        let malloc_fn = if let Some(func) = self.module.get_function("malloc") {
            func
        } else {
            self.module.add_function("malloc", malloc_fn_type, None)
        };

        let result_str = self
            .builder
            .build_call(malloc_fn, &[total_with_null.into()], "result_str")?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_pointer_value();

        // Initialize loop counter
        let idx_alloca = self.builder.build_alloca(i64_type, "idx")?;
        self.builder
            .build_store(idx_alloca, i64_type.const_int(0, false))?;

        self.builder.build_unconditional_branch(copy_loop)?;

        // Copy loop: convert each character to uppercase
        self.builder.position_at_end(copy_loop);
        let idx = self
            .builder
            .build_load(i64_type, idx_alloca, "idx")?
            .into_int_value();

        // Check if idx < str_len
        let continue_copy = self.builder.build_int_compare(
            IntPredicate::SLT,
            idx,
            str_len,
            "continue_copy",
        )?;
        self.builder
            .build_conditional_branch(continue_copy, copy_body, copy_loop_end)?;

        // Copy body: convert character to uppercase
        self.builder.position_at_end(copy_body);

        // Get current character from original string
        let current_char_ptr = unsafe {
            self.builder
                .build_gep(i8_type, original_str_ptr, &[idx], "current_char_ptr")?
        };
        let current_char = self
            .builder
            .build_load(i8_type, current_char_ptr, "current_char")?
            .into_int_value();

        // Convert to uppercase: if char is lowercase (a-z), subtract 32
        // ASCII: 'a' = 97, 'z' = 122, 'A' = 65, 'Z' = 90
        let is_lowercase = self.builder.build_and(
            self.builder.build_int_compare(
                IntPredicate::SGE,
                current_char,
                i8_type.const_int(97, false), // 'a'
                "ge_a",
            )?,
            self.builder.build_int_compare(
                IntPredicate::SLE,
                current_char,
                i8_type.const_int(122, false), // 'z'
                "le_z",
            )?,
            "is_lowercase",
        )?;

        // If lowercase, subtract 32; otherwise keep as is
        let uppercase_char = self.builder.build_select(
            is_lowercase,
            self.builder.build_int_sub(
                current_char,
                i8_type.const_int(32, false),
                "uppercase_char",
            )?,
            current_char,
            "final_char",
        )?;

        // Store in result string
        let result_char_ptr = unsafe {
            self.builder
                .build_gep(i8_type, result_str, &[idx], "result_char_ptr")?
        };
        self.builder.build_store(result_char_ptr, uppercase_char)?;

        // Increment index
        let next_idx = self
            .builder
            .build_int_add(idx, i64_type.const_int(1, false), "next_idx")?;
        self.builder.build_store(idx_alloca, next_idx)?;

        self.builder.build_unconditional_branch(copy_loop)?;

        // Copy loop end: add null terminator and jump to return
        self.builder.position_at_end(copy_loop_end);
        let null_ptr = unsafe {
            self.builder
                .build_gep(i8_type, result_str, &[str_len], "null_ptr")?
        };
        self.builder
            .build_store(null_ptr, i8_type.const_int(0, false))?;

        self.builder.build_unconditional_branch(return_block)?;

        // Return block with PHI node
        self.builder.position_at_end(return_block);
        let result_phi = self.builder.build_phi(i8_ptr_type, "to_upper_result")?;

        // Empty string case: return ""
        let empty_str = self.builder.build_global_string_ptr("", "empty_str")?;
        result_phi.add_incoming(&[(&empty_str, empty_check_block)]);

        // Normal case: return converted string
        result_phi.add_incoming(&[(&result_str, copy_loop_end)]);

        Ok(result_phi.as_basic_value())
    }


    /// Generate string.to_lowercase() method call
    /// Converts the string to lowercase and returns the result
    ///
    /// Example: "HELLO".to_lowercase() returns "hello"
    pub(in crate::llvm_backend) fn generate_string_to_lowercase_method(
        &mut self,
        var_name: &str,
    ) -> Result<BasicValueEnum<'ctx>> {
        // Get the string pointer
        let string_ptr = if let Some(var_value) = self.variable_map.get(var_name) {
            var_value.into_pointer_value()
        } else {
            return Err(CompilerError::internal(&format!(
                "Undefined variable: {}",
                var_name
            )));
        };

        // Load the string pointer value (i8*)
        let i8_ptr_type = self.context.i8_type().ptr_type(AddressSpace::default());
        let string_value = self
            .builder
            .build_load(i8_ptr_type, string_ptr, "string_val")?;
        let original_str_ptr = string_value.into_pointer_value();

        let i8_type = self.context.i8_type();
        let i32_type = self.context.i32_type();
        let i64_type = self.context.i64_type();

        // Get strlen function
        let strlen_fn_type = i32_type.fn_type(&[i8_ptr_type.into()], false);
        let strlen_fn = if let Some(func) = self.module.get_function("strlen") {
            func
        } else {
            self.module.add_function("strlen", strlen_fn_type, None)
        };

        // Get string length
        let str_len_i32 = self
            .builder
            .build_call(strlen_fn, &[original_str_ptr.into()], "str_len_i32")?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_int_value();
        let str_len = self
            .builder
            .build_int_z_extend(str_len_i32, i64_type, "str_len")?;

        // Create basic blocks
        let current_func = self.current_function.unwrap();
        let empty_check_block = self
            .context
            .append_basic_block(current_func, "to_lower_empty_check");
        let alloc_block = self
            .context
            .append_basic_block(current_func, "to_lower_alloc");
        let copy_loop = self
            .context
            .append_basic_block(current_func, "to_lower_copy_loop");
        let copy_body = self
            .context
            .append_basic_block(current_func, "to_lower_copy_body");
        let copy_loop_end = self
            .context
            .append_basic_block(current_func, "to_lower_copy_loop_end");
        let return_block = self
            .context
            .append_basic_block(current_func, "to_lower_return");

        // Jump to empty check
        self.builder.build_unconditional_branch(empty_check_block)?;

        // Empty check: if len == 0, return empty string
        self.builder.position_at_end(empty_check_block);
        let is_empty = self.builder.build_int_compare(
            IntPredicate::EQ,
            str_len,
            i64_type.const_int(0, false),
            "is_empty",
        )?;
        self.builder
            .build_conditional_branch(is_empty, return_block, alloc_block)?;

        // Allocate memory: str_len + 1 (for null terminator)
        self.builder.position_at_end(alloc_block);
        let total_with_null =
            self.builder
                .build_int_add(str_len, i64_type.const_int(1, false), "total_with_null")?;

        // Allocate memory using malloc
        let malloc_fn_type = i8_ptr_type.fn_type(&[i64_type.into()], false);
        let malloc_fn = if let Some(func) = self.module.get_function("malloc") {
            func
        } else {
            self.module.add_function("malloc", malloc_fn_type, None)
        };

        let result_str = self
            .builder
            .build_call(malloc_fn, &[total_with_null.into()], "result_str")?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_pointer_value();

        // Initialize loop counter
        let idx_alloca = self.builder.build_alloca(i64_type, "idx")?;
        self.builder
            .build_store(idx_alloca, i64_type.const_int(0, false))?;

        self.builder.build_unconditional_branch(copy_loop)?;

        // Copy loop: convert each character to lowercase
        self.builder.position_at_end(copy_loop);
        let idx = self
            .builder
            .build_load(i64_type, idx_alloca, "idx")?
            .into_int_value();

        // Check if idx < str_len
        let continue_copy = self.builder.build_int_compare(
            IntPredicate::SLT,
            idx,
            str_len,
            "continue_copy",
        )?;
        self.builder
            .build_conditional_branch(continue_copy, copy_body, copy_loop_end)?;

        // Copy body: convert character to lowercase
        self.builder.position_at_end(copy_body);

        // Get current character from original string
        let current_char_ptr = unsafe {
            self.builder
                .build_gep(i8_type, original_str_ptr, &[idx], "current_char_ptr")?
        };
        let current_char = self
            .builder
            .build_load(i8_type, current_char_ptr, "current_char")?
            .into_int_value();

        // Convert to lowercase: if char is uppercase (A-Z), add 32
        // ASCII: 'A' = 65, 'Z' = 90, 'a' = 97, 'z' = 122
        let is_uppercase = self.builder.build_and(
            self.builder.build_int_compare(
                IntPredicate::SGE,
                current_char,
                i8_type.const_int(65, false), // 'A'
                "ge_A",
            )?,
            self.builder.build_int_compare(
                IntPredicate::SLE,
                current_char,
                i8_type.const_int(90, false), // 'Z'
                "le_Z",
            )?,
            "is_uppercase",
        )?;

        // If uppercase, add 32; otherwise keep as is
        let lowercase_char = self.builder.build_select(
            is_uppercase,
            self.builder.build_int_add(
                current_char,
                i8_type.const_int(32, false),
                "lowercase_char",
            )?,
            current_char,
            "final_char",
        )?;

        // Store in result string
        let result_char_ptr = unsafe {
            self.builder
                .build_gep(i8_type, result_str, &[idx], "result_char_ptr")?
        };
        self.builder.build_store(result_char_ptr, lowercase_char)?;

        // Increment index
        let next_idx = self
            .builder
            .build_int_add(idx, i64_type.const_int(1, false), "next_idx")?;
        self.builder.build_store(idx_alloca, next_idx)?;

        self.builder.build_unconditional_branch(copy_loop)?;

        // Copy loop end: add null terminator and jump to return
        self.builder.position_at_end(copy_loop_end);
        let null_ptr = unsafe {
            self.builder
                .build_gep(i8_type, result_str, &[str_len], "null_ptr")?
        };
        self.builder
            .build_store(null_ptr, i8_type.const_int(0, false))?;

        self.builder.build_unconditional_branch(return_block)?;

        // Return block with PHI node
        self.builder.position_at_end(return_block);
        let result_phi = self.builder.build_phi(i8_ptr_type, "to_lower_result")?;

        // Empty string case: return ""
        let empty_str = self.builder.build_global_string_ptr("", "empty_str")?;
        result_phi.add_incoming(&[(&empty_str, empty_check_block)]);

        // Normal case: return converted string
        result_phi.add_incoming(&[(&result_str, copy_loop_end)]);

        Ok(result_phi.as_basic_value())
    }


    /// Generate string.capitalize() method call
    /// Converts the first character to uppercase and the rest to lowercase
    ///
    /// Example: "hello WORLD".capitalize() returns "Hello world"
    pub(in crate::llvm_backend) fn generate_string_capitalize_method(
        &mut self,
        var_name: &str,
    ) -> Result<BasicValueEnum<'ctx>> {
        // Get the string pointer
        let string_ptr = if let Some(var_value) = self.variable_map.get(var_name) {
            var_value.into_pointer_value()
        } else {
            return Err(CompilerError::internal(&format!(
                "Undefined variable: {}",
                var_name
            )));
        };

        // Load the string pointer value (i8*)
        let i8_ptr_type = self.context.i8_type().ptr_type(AddressSpace::default());
        let string_value = self
            .builder
            .build_load(i8_ptr_type, string_ptr, "string_val")?;
        let original_str_ptr = string_value.into_pointer_value();

        let i8_type = self.context.i8_type();
        let i32_type = self.context.i32_type();
        let i64_type = self.context.i64_type();

        // Get strlen function
        let strlen_fn_type = i32_type.fn_type(&[i8_ptr_type.into()], false);
        let strlen_fn = if let Some(func) = self.module.get_function("strlen") {
            func
        } else {
            self.module.add_function("strlen", strlen_fn_type, None)
        };

        // Get string length
        let str_len_i32 = self
            .builder
            .build_call(strlen_fn, &[original_str_ptr.into()], "str_len_i32")?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_int_value();
        let str_len = self
            .builder
            .build_int_z_extend(str_len_i32, i64_type, "str_len")?;

        // Create basic blocks
        let current_func = self.current_function.unwrap();
        let empty_check_block = self
            .context
            .append_basic_block(current_func, "capitalize_empty_check");
        let alloc_block = self
            .context
            .append_basic_block(current_func, "capitalize_alloc");
        let copy_loop = self
            .context
            .append_basic_block(current_func, "capitalize_copy_loop");
        let copy_body = self
            .context
            .append_basic_block(current_func, "capitalize_copy_body");
        let copy_loop_end = self
            .context
            .append_basic_block(current_func, "capitalize_copy_loop_end");
        let return_block = self
            .context
            .append_basic_block(current_func, "capitalize_return");

        // Jump to empty check
        self.builder.build_unconditional_branch(empty_check_block)?;

        // Empty check: if len == 0, return empty string
        self.builder.position_at_end(empty_check_block);
        let is_empty = self.builder.build_int_compare(
            IntPredicate::EQ,
            str_len,
            i64_type.const_int(0, false),
            "is_empty",
        )?;
        self.builder
            .build_conditional_branch(is_empty, return_block, alloc_block)?;

        // Allocate memory: str_len + 1 (for null terminator)
        self.builder.position_at_end(alloc_block);
        let total_with_null =
            self.builder
                .build_int_add(str_len, i64_type.const_int(1, false), "total_with_null")?;

        // Allocate memory using malloc
        let malloc_fn_type = i8_ptr_type.fn_type(&[i64_type.into()], false);
        let malloc_fn = if let Some(func) = self.module.get_function("malloc") {
            func
        } else {
            self.module.add_function("malloc", malloc_fn_type, None)
        };

        let result_str = self
            .builder
            .build_call(malloc_fn, &[total_with_null.into()], "result_str")?
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_pointer_value();

        // Initialize loop counter
        let idx_alloca = self.builder.build_alloca(i64_type, "idx")?;
        self.builder
            .build_store(idx_alloca, i64_type.const_int(0, false))?;

        self.builder.build_unconditional_branch(copy_loop)?;

        // Copy loop: convert first character to uppercase, rest to lowercase
        self.builder.position_at_end(copy_loop);
        let idx = self
            .builder
            .build_load(i64_type, idx_alloca, "idx")?
            .into_int_value();

        // Check if idx < str_len
        let continue_copy = self.builder.build_int_compare(
            IntPredicate::SLT,
            idx,
            str_len,
            "continue_copy",
        )?;
        self.builder
            .build_conditional_branch(continue_copy, copy_body, copy_loop_end)?;

        // Copy body: convert character based on position
        self.builder.position_at_end(copy_body);

        // Get current character from original string
        let current_char_ptr = unsafe {
            self.builder
                .build_gep(i8_type, original_str_ptr, &[idx], "current_char_ptr")?
        };
        let current_char = self
            .builder
            .build_load(i8_type, current_char_ptr, "current_char")?
            .into_int_value();

        // Check if this is the first character (idx == 0)
        let is_first_char = self.builder.build_int_compare(
            IntPredicate::EQ,
            idx,
            i64_type.const_int(0, false),
            "is_first_char",
        )?;

        // For first character: convert to uppercase if lowercase
        let is_lowercase = self.builder.build_and(
            self.builder.build_int_compare(
                IntPredicate::SGE,
                current_char,
                i8_type.const_int(97, false), // 'a'
                "ge_a",
            )?,
            self.builder.build_int_compare(
                IntPredicate::SLE,
                current_char,
                i8_type.const_int(122, false), // 'z'
                "le_z",
            )?,
            "is_lowercase",
        )?;

        let first_char_uppercase = self.builder.build_select(
            is_lowercase,
            self.builder.build_int_sub(
                current_char,
                i8_type.const_int(32, false),
                "uppercase_char",
            )?,
            current_char,
            "first_char_result",
        )?;

        // For other characters: convert to lowercase if uppercase
        let is_uppercase = self.builder.build_and(
            self.builder.build_int_compare(
                IntPredicate::SGE,
                current_char,
                i8_type.const_int(65, false), // 'A'
                "ge_A",
            )?,
            self.builder.build_int_compare(
                IntPredicate::SLE,
                current_char,
                i8_type.const_int(90, false), // 'Z'
                "le_Z",
            )?,
            "is_uppercase",
        )?;

        let other_char_lowercase = self.builder.build_select(
            is_uppercase,
            self.builder.build_int_add(
                current_char,
                i8_type.const_int(32, false),
                "lowercase_char",
            )?,
            current_char,
            "other_char_result",
        )?;

        // Select final character based on position
        let final_char = self.builder.build_select(
            is_first_char,
            first_char_uppercase,
            other_char_lowercase,
            "final_char",
        )?;

        // Store in result string
        let result_char_ptr = unsafe {
            self.builder
                .build_gep(i8_type, result_str, &[idx], "result_char_ptr")?
        };
        self.builder.build_store(result_char_ptr, final_char)?;

        // Increment index
        let next_idx = self
            .builder
            .build_int_add(idx, i64_type.const_int(1, false), "next_idx")?;
        self.builder.build_store(idx_alloca, next_idx)?;

        self.builder.build_unconditional_branch(copy_loop)?;

        // Copy loop end: add null terminator and jump to return
        self.builder.position_at_end(copy_loop_end);
        let null_ptr = unsafe {
            self.builder
                .build_gep(i8_type, result_str, &[str_len], "null_ptr")?
        };
        self.builder
            .build_store(null_ptr, i8_type.const_int(0, false))?;

        self.builder.build_unconditional_branch(return_block)?;

        // Return block with PHI node
        self.builder.position_at_end(return_block);
        let result_phi = self.builder.build_phi(i8_ptr_type, "capitalize_result")?;

        // Empty string case: return ""
        let empty_str = self.builder.build_global_string_ptr("", "empty_str")?;
        result_phi.add_incoming(&[(&empty_str, empty_check_block)]);

        // Normal case: return converted string
        result_phi.add_incoming(&[(&result_str, copy_loop_end)]);

        Ok(result_phi.as_basic_value())
    }
}
